-
Notifications
You must be signed in to change notification settings - Fork 5
feat: committor service #366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
- at this point schedule commit tests pass with maximum concurrency
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
113 file(s) reviewed, 128 comment(s)
Edit PR Review Bot Settings | Greptile
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job at documenting the code so thoroughly, left some nits, I have to trust that the logic is sane and the test suite covers all the edge case, as trying to completely understand the entire interplay of all the components would take prohibitive amount of time.
res.await | ||
.map_err(|err| { | ||
// Send request error | ||
AccountClonerError::CommittorSerivceError(format!("{:?}", err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AccountClonerError::CommittorSerivceError(format!("{:?}", err)) | |
AccountClonerError::CommittorSerivceError(format!("error sending request {err:?}")) |
res.await | ||
.map_err(|err| { | ||
// Send request error | ||
AccountClonerError::CommittorSerivceError(format!("{:?}", err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AccountClonerError::CommittorSerivceError(format!("{:?}", err)) | |
AccountClonerError::CommittorSerivceError(format!("error sending request {err:?}")) | |
```suggestion | |
AccountClonerError::CommittorSerivceError(format!("{:?}", err)) |
})? | ||
.map_err(|err| { | ||
// Commit error | ||
AccountClonerError::CommittorSerivceError(format!("{:?}", err)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AccountClonerError::CommittorSerivceError(format!("{:?}", err)) | |
AccountClonerError::CommittorSerivceError(format!("error during commit {err:?}")) |
let max_slot = scheduled_commits | ||
.iter() | ||
.map(|commit| commit.slot) | ||
.max() | ||
.unwrap(); | ||
// Safety we just obtained the max slot from the scheduled commits | ||
let ephemeral_blockhash = scheduled_commits | ||
.iter() | ||
.find(|commit| commit.slot == max_slot) | ||
.map(|commit| commit.blockhash) | ||
.unwrap(); | ||
|
||
changeset.slot = max_slot; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let max_slot = scheduled_commits | |
.iter() | |
.map(|commit| commit.slot) | |
.max() | |
.unwrap(); | |
// Safety we just obtained the max slot from the scheduled commits | |
let ephemeral_blockhash = scheduled_commits | |
.iter() | |
.find(|commit| commit.slot == max_slot) | |
.map(|commit| commit.blockhash) | |
.unwrap(); | |
changeset.slot = max_slot; | |
let mut changeset = Changeset::default(); | |
let Some(commit) = scheduled_commits.iter().max_by_key(|commit| commit.slot) else { | |
return Ok(()); | |
}; | |
let max_slot = commit.slot; | |
let ephemeral_blockhash = commit.blockhash; | |
changeset.slot = max_slot; |
msg!("Error: {:?}", e); | ||
use CommittorError::*; | ||
let n = match e { | ||
UnableToSerializeChangeSet(_) => 0x69000, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are those magic numbers? Some explanatory comments would be helpful
/// - *returns* `true` if the pubkey could be reserved | ||
fn reserve(&self, pubkey: &Pubkey) -> bool { | ||
if let Some(count) = self.pubkeys.get(pubkey) { | ||
count.fetch_add(1, Ordering::SeqCst); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SeqCst is a total overkill, Relaxed would be totally correct as we are not synchronizing any other state through this atomic
fn release(&self, pubkey: &Pubkey) -> bool { | ||
if let Some(count) = self.pubkeys.get(pubkey) { | ||
count | ||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto, SeqCst is an overkill, since no other state is synchronized
fn has_reservations(&self) -> bool { | ||
self.pubkeys | ||
.values() | ||
.any(|rc_pubkey| rc_pubkey.load(Ordering::SeqCst) > 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto, Ordering::Relaxed is enough
}) | ||
} | ||
|
||
/// Returns `true` if the we requested to deactivate this table. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// Returns `true` if the we requested to deactivate this table. | |
/// Returns `true` if we have already requested to deactivate this table. |
|
||
#[derive(Clone)] | ||
pub struct TableMania { | ||
pub active_tables: Arc<RwLock<Vec<LookupTableRc>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess some research was made to justify the usage of async synchronization primitives, i.e. they will be used in highly concurrent environments and with lock guards being held across await points, if not, then their use is unjustified.
Summary
Adding a full fledged committor service to support the following kind of commits:
easy to manually retry them or do this automatically (next committor version will support this)
Details
The following crates were developed
here and integrated
into the validator:
magicblock-rpc-client
signature status checking (including sane retries)
magicblock-table-mania
used in a transaction the needed table addresses are provided
all its pubkeys are released
magicblock-committor-program
(out of order is ok)
magicblock-committor-service
available
Sample Flow
service
lookup tables which we might need as fast as possible
is provided to the user via logs in a transaction (as we did before, just now we have to wait
for the commit to complete)
For those commits the committor service ensures that accounts with the same commit (bundle) id
are always processed atomically.
The committor service also picks the best possible strategy to commit each changeset,
preferring speed and then cost.
It also inserts configurable (via code) compute budget instructions to each transaction.
The outcome of each commit is persisted to a database which allows manual (and in the next
version) automated retries of failed commits.
Succeessful commits are also persisted to the database and can be used for diagnostics. In the
future they should be removed since no retry is necessary.
On chain signatures for process-commit/finaliz/undelegate transactions are also persisted to
the database in a separate table.
This table is queried by the validator to log those signatures as part of a transaction that
the user waits for.
Greptile Summary
This PR introduces a comprehensive committor service to the MagicBlock validator for managing account changes and transactions. The implementation includes a new Solana program for handling commits, a service layer for managing commit operations, and integration with lookup tables for transaction optimization.
magicblock-committor-program
that handles chunked account data commits with proper buffer management and security checksmagicblock-committor-service
that orchestrates commit operations with support for bundling, lookup tables, and persistent status tracking via SQLitemagicblock-table-mania
for managing address lookup tables to optimize transaction sizesmagicblock-rpc-client
with enhanced transaction handling and batched account fetching